﻿#region Copyright (c) Nicola Gallo All rights reserved.
/*
 * All rights reserved. Reproduction of trasmission of this file, or a portion thereof,
 * is forbidden without prior written permission of Nicola Gallo.
 * 
 * Author: Nicola Gallo
 * Date: 09/04/2011
 * 
 * */
#endregion
namespace PrismMVC.Shell
{
    //.NET References
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;

    //Microsoft References
    using Microsoft.Practices.Prism.Modularity;
    using Microsoft.Practices.Prism.Logging;
    using Microsoft.Practices.Prism.Events;
    using Microsoft.Practices.Unity;

    //PrismMVC References
    using PrismMVC.Shell.Containers;
    using PrismMVC.Shell.MVC;

    /// <summary>
    /// Represents an application module.
    /// </summary>
    public class Module : IModule
    {
        //Constructors

        /// <summary>
        /// Creates a new instance of <see cref="Module"/>.
        /// </summary>
        /// <param name="unityContainer">Unity container.</param>
        /// <param name="shellProxy">Shell proxy.</param>
        /// <param name="loggerFacade">Logger facade.</param>
        /// <param name="eventAggregator">Event aggregator.</param>
        public Module(IUnityContainer unityContainer, IShellProxy shellProxy, ILoggerFacade loggerFacade, IEventAggregator eventAggregator)
        {
            if (unityContainer == null)
            {
                throw new ArgumentNullException("UnityContainer parameter can not be equals to null.");
            }
            else if (shellProxy == null)
            {
                throw new ArgumentNullException("ShellProxy parameter can not be equals to null.");
            }
            else if (loggerFacade == null)
            {
                throw new ArgumentNullException("LoggerFacade parameter can not be equals to null.");
            }
            else if (eventAggregator == null)
            {
                throw new ArgumentNullException("EventAggregator parameter can not be equals to null.");
            }
            this.UnityContainer = unityContainer;
            this.ShellProxy = shellProxy;
            this.LoggerFacade = loggerFacade;
            this.EventAggregator = eventAggregator;
        }

        //Properties

        /// <summary>
        /// Gets the shell proxy.
        /// </summary>
        private IShellProxy ShellProxy { get; set; }

        /// <summary>
        /// Gets the shell proxy.
        /// </summary>
        private IUnityContainer UnityContainer { get; set; }

        /// <summary>
        /// Gets the logger facade.
        /// </summary>
        protected ILoggerFacade LoggerFacade { get; private set; }

        /// <summary>
        /// Gets the event aggregator.
        /// </summary>
        protected IEventAggregator EventAggregator { get; private set; }

        //Methods

        /// <summary>
        /// Creates a new instance of <see cref="Controller"/>.
        /// </summary>
        /// <typeparam name="T">Type of controller.</typeparam>
        /// <returns>A new instance of controller.</returns>
        private T CreateController<T>() where T : IController
        {
            T controller = this.UnityContainer.Resolve<T>();
            if (controller is Controller)
            {
                String ViewDataID = Guid.NewGuid().ToString();
                (controller as Controller).SetsViewDataID(ViewDataID);   
            }
            return controller;
        }

        /// <summary>
        /// Run a new instance of <see cref="Controller"/>.
        /// </summary>
        /// <typeparam name="T">Type of controller.</typeparam>
        /// <param name="regionName">Region name.</param>
        /// <param name="args">Action name.</param>
        public void RunController<T>(String actionName, String regionName) where T : IController 
        {
            this.RunController<T>(actionName, null, regionName);
        }

        /// <summary>
        /// Run a new instance of <see cref="Controller"/>.
        /// </summary>
        /// <typeparam name="T">Type of controller.</typeparam>
        /// <param name="args">Action name.</param>
        /// <param name="args">Arguments.</param>
        /// <param name="regionName">Region name.</param>
        public void RunController<T>(String actionName, Object args, String regionName) where T : IController
        {
            if (string.IsNullOrWhiteSpace(regionName))
            {
                throw new ArgumentNullException("RegionName can not be null.");
            }
            T controller = this.CreateController<T>();
            if (controller is Controller)
            {
                (controller as Controller).RegionName = regionName;
                (controller as Controller).Module = this;
                (controller as Controller).SetsViewDataID(Guid.NewGuid().ToString());
            }
            controller.Navigating -= new EventHandler<ControllerEventArgs>(Controller_Navigating);
            controller.Navigating += new EventHandler<ControllerEventArgs>(Controller_Navigating);
            controller.InvokeAction(actionName, args);
            controller.Initialize();
            controller.Run();
        }

        /// <summary>
        /// Occurs when there is a navigation.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Controller_Navigating(object sender, ControllerEventArgs e)
        {
            Controller controller = sender as Controller;
            if (controller != null)
            {
                View view = e.CurrentState.View as View;
                if (view != null)
                {
                    foreach (PresentationModel presentationModel in view.PresentationModels)
                    {
                        presentationModel.Controller = controller;
                        presentationModel.SetsViewDataID(view.ViewDataID);
                        presentationModel.Initialize();
                    }
                }
                if (controller.ViewHost == null)
                {
                    controller.ViewHost = new View();
                    this.ShellProxy.AddView(controller.RegionName, controller.ViewHost);
                }
                (controller.ViewHost as View).Content = e.CurrentState.View;
            }
        }

        /// <summary>
        ///  Notifies the module that it has be initialized.
        /// </summary>
        public virtual void Initialize() { }
    }
}